// Copyright 2014 The Go Circuit Project
// Use of this source code is governed by the license for
// The Go Circuit Project, found in the LICENSE file.
//
// Authors:
//   2014 Chris Monson <chris@gocircuit.org>

package ripple

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"sync"
	"time"

	"circuit/kit/rh"
)

type GenericReader struct {
	rh.AnchoredFID
	sync.Mutex

	q   rh.Q
	gtm time.Time

	name     string
	dataFunc func() ([]byte, error)
	output   *bytes.Buffer
}

func NewGenericReader(name string, read func() ([]byte, error)) rh.FID {
	return &GenericReader{name: name, dataFunc: read}
}

func (r *GenericReader) String() string {
	return fmt.Sprintf("%s (%p) %v", r.name, r, r.Q())
}

func (r *GenericReader) Q() rh.Q {
	return r.q
}

func (r *GenericReader) Open(flag rh.Flag, intr rh.Intr) error {
	return nil
}

func (r *GenericReader) Read(offset int64, count int, intr rh.Intr) (rh.Chunk, error) {
	defer un(lock(r))
	log.Printf("%s Read: %d %d", r.name, offset, count)

	if r.output == nil {
		data, err := r.dataFunc()
		if err != nil {
			return 0, err
		}
		r.output = bytes.NewBuffer(data)
		r.output.WriteByte('\n')
	}

	// Special case for returning all (remaining) data.
	if count == 0 {
		chunk := rh.ByteChunk(r.output.Bytes())
		r.output.Reset()
		return chunk, nil
	}

	buff := make([]byte, count)
	n, err := r.output.Read(buff) // ignore offset
	if err == io.EOF {
		err = nil // Fuse gets very confused about EOF errors
	}
	return rh.ByteChunk(buff[:n]), err
}

func (r *GenericReader) Write(offset int64, data rh.Chunk, intr rh.Intr) (int, error) {
	return 0, rh.ErrPerm
}

func (r *GenericReader) Clunk() {
}

func (r *GenericReader) Walk(wname []string) (rh.FID, error) {
	if len(wname) > 0 {
		return nil, rh.ErrClash
	}
	return NewGenericReader(r.name, r.dataFunc), nil
}

func (r *GenericReader) Stat() (*rh.Dir, error) {
	return &rh.Dir{
		Q:      r.q,
		Mode:   rh.Mode{Attr: rh.ModeIO},
		Perm:   0444, // -r--r--r-
		Atime:  r.gtm,
		Mtime:  r.gtm,
		Name:   r.name,
		Length: 0, // TODO(chris): emit a real size?
		Uid:    uid(),
		Gid:    gid(),
	}, nil
}
